如果有自己编译过AOSP的源码,可能大家都会遇到这样的一个问题:
1 | ninja: error: '*******', needed by '*********', missing and no known rule to make it |
在刚刚开始接触AOSP的时候,我基本上都会直奔主题,大概清楚问题后,然后解决问题、重新编译,所以一直都没有注意到一个关键字:”Ninja”,今天我们就一起来学习/实践一下这个”熟悉的陌生人”,主要介绍一下目前Android的编译系统,以及以一个小Demo为例,实践一下Ninja的使用,如果想了解更多关于Ninja在AOSP中的使用,见参考链接。
1、Android编译系统
早期的Android系统采用Android.mk的配置来编译源码,从Android 7.0开始引入Android.bp。大体梳理一下Android版本相应的发展演变过程:
- Android 7.0引入ninja和kati
- Android 8.0使用Android.bp来替换Android.mk,引入Soong
- Android 9.0强制使用Android.bp
上图就是各个文件之间的转化关系,这里涉及到Ninja, kati, Soong, bp概念,简单介绍一下:
1. Ninja
ninja是一个编译框架,会根据相应的ninja格式的配置文件进行编译,但是ninja文件一般不会手动修改,而是通过将Android.bp文件转换成ninja格文件来编译。
2. Android.bp
Android.bp的出现就是为了替换Android.mk文件。bp跟mk文件不同,它是纯粹的配置,没有分支、循环等流程控制,不能做算数逻辑运算。如果需要控制逻辑,那么只能通过Go语言编写。
3. Soong
Soong类似于之前的Makefile编译系统的核心,负责提供Android.bp语义解析,并将之转换成Ninja文件。Soong还会编译生成一个androidmk命令,用于将Android.mk文件转换为Android.bp文件,不过这个转换功能仅限于没有分支、循环等流程控制的Android.mk才有效。
4. Blueprint
Blueprint是生成、解析Android.bp的工具,是Soong的一部分。Soong负责Android编译而设计的工具,而Blueprint只是解析文件格式,Soong解析内容的具体含义。Blueprint和Soong都是由Golang写的项目,从Android 7.0,prebuilts/go/目录下新增Golang所需的运行环境,在编译时使用。
5. Kati
kati是专为Android开发的一个基于Golang和C++的工具,主要功能是把Android中的Android.mk文件转换成Ninja文件。代码路径是build/kati/,编译后的产物是ckati。
2、Ninja的介绍
从上面我们可以看到,Android最后的编译工作都会交给Ninja,那么它到底是个什么东西呢,为什么Google需要在Android引入Ninja呢?
Ninja is a small build system with a focus on speed. It differs from other build systems in two major respects: it is designed to have its input files generated by a higher-level build system, and it is designed to run builds as fast as possible.
Ninja是一个专注于速度的构建系统,和其他构建系统相比,主要有两点不同:
- Ninja的输入文件一般都是有更高级的构建系统产生的,比如cmake;
- Ninja设计之初就是为了更快的构建;
其实从第一点,我们就能看出来Ninja的设计哲学:相比Makefile是设计出来给人手写的,但是Ninja设计出来是给其它程序生成的。 如果说Makefile是C语言,那么Ninja就是汇编语言。 如果说Makefile是一个DSL,那么Ninja就是一种配置文件。 Makefile支持分支、循环等流程控制,而Ninja只支持一些固定形式的配置。
3、Ninja的实践
工欲善其事,必先利其器,所以我们需要先准备好相关工具:CMake,ninja,以Mac OS为例,安装步骤为:
1 | brew install cmake |
正式开始,以一个最简单的Hello,Ninja为例:
- main.cpp
1 |
|
- CMakeLists.txt
1 | cmake_minimum_required(VERSION 3.13) |
OK,一个简单的Hello,Ninja的C++工程已经完成,接下来我们开始编译,使用如下命令,生成build.ninja
文件:
1 | [min@bogon:] ninja $ cmake -G Ninja -B build . |
可以看到在build
目录下,生成了如下文件:
1 | [min@bogon:] build $ tree -L 2 |
其中最重要的就是这个build.ninja
文件,接下来,我们使用Ninja命令,开始编译:
1 | [min@bogon:] ninja $ cd build/ |
至此,一个简单的Hello, Ninja!就已经实践完成了,具体关于AOSP中Ninja的编译,希望大家可以自己去摸索一下~